package limiter

import (
	"log"
	"sync"
	"time"
)

type GBRateLimitSet struct {
	name            interface{}
	checkIntervals  int
	elementsLock    *sync.Mutex
	elements        map[interface{}]*GBRateLimiter
}

// 创建周期速率限制对象
//
// @param name 对象名称
// @param checkIntervals 周期间隔（秒数），检测周期越长，内存压力越大，GC 压力越小
//
// @return 新对象
//
func GBNewRateLimitSet(name interface{}, checkIntervals int) * GBRateLimitSet {
	// 分配对象
	r := &GBRateLimitSet{
		name:           name,
		checkIntervals: checkIntervals,
		elementsLock:   &sync.Mutex{},
		elements:       make(map[interface{}]*GBRateLimiter, 0),
	}
	// 启动检测循环
	go r.checkExpireLoop()
	// 创建完成
	return r
}

// 检测过期一圈
func (obj *GBRateLimitSet) checkExpireOnce() {
	// 加锁访问
	obj.elementsLock.Lock()
	defer obj.elementsLock.Unlock()
	// 当前大小
	sizebeg := len(obj.elements)
	// 遍历删除过期的
	for k, v := range obj.elements {
		if v.Expired() {
			delete(obj.elements, k)
		}
	}
	// 当前大小
	sizeend := len(obj.elements)
	// 随便打个日志
	log.Printf("GBRateLimitSet [%v] - expired : %d, current : %d.", obj.name, sizebeg - sizeend, sizeend)
}

// 检测过期循环
func (obj *GBRateLimitSet) checkExpireLoop() {

	for {
		// 固定周期检测，暂不考虑动态调整
		time.Sleep(time.Duration(obj.checkIntervals) * time.Second)
		// 定时检测一次
		obj.checkExpireOnce()
	}
}

// 获取对象如果不存在自动创建，如果存在，则判断超时
func (obj *GBRateLimitSet) obtainElementOrCreate(key interface{}) *GBRateLimiter {
	// 已存在直接返回
	r, ok := obj.elements[key]
	if ok {
		return r
	}
	// 不存在自动创建
	r = GBNewRateLimiter()
	// 保存对象
	obj.elements[key] = r
	// 创建成功
	return r
}

// 取得名称
func (obj *GBRateLimitSet) Name() interface{} {
	return obj.name
}

// 取得当前周期剩余额度
//
// @param intervals 周期间隔（秒数）
// @param limit 每周期最大额度
//
// @return 剩余额度数量，可能是负数
//
func (obj *GBRateLimitSet) GetLeft(key interface{}, intervals, limit int) int {
	// 参数错误
	if intervals < 1 || limit == 0 {
		return 0
	}
	// 加锁
	obj.elementsLock.Lock()
	defer obj.elementsLock.Unlock()
	// 查找对象
	v, ok := obj.elements[key]
	// 对象不存在
	if !ok {
		return limit
	}
	// 返回剩余数量
	return v.GetLeft(intervals, limit)
}

// 周期性速率限制检测，是否被限制
//
// @param key 目标
// @param intervals 周期间隔（秒数）
// @param limit 每周期最大额度
//
// @return true：被限制，false：未被限制
//
func (obj *GBRateLimitSet) IsLimited(key interface{}, intervals, limit int) bool {
	// 参数错误
	if intervals < 1 || limit < 1 {
		return true
	}
	// 加锁
	obj.elementsLock.Lock()
	defer obj.elementsLock.Unlock()
	// 查找对象
	v, ok := obj.elements[key]
	// 对象不存在
	if !ok {
		return false
	}
	// 检测
	return v.IsLimited(intervals, limit)
}

// 周期性速率限制检测
//
// @param key 目标
// @param intervals 周期间隔（秒数）
// @param limit 每周期最大额度
// @param consume 本次消费额度，可以传"0"表示不消费
//
// @return true：被限制，false：未被限制，当实际消费额度大于"0"时，一定会返回"false"
// @return 本次实际消费额度
// @return 本周期剩余额度
//
func (obj *GBRateLimitSet) CheckLimit(key interface{}, intervals, limit, consume int) (bool, int, int) {
	// 参数错误
	if intervals < 1 || limit < 1 {
		return true, 0, 0
	}
	// 加锁
	obj.elementsLock.Lock()
	defer obj.elementsLock.Unlock()
	// 获取对象
	v := obj.obtainElementOrCreate(key)
	// 检测
	return v.CheckLimit(intervals, limit, consume)
}

// 周期性速率限制检测，是否被限制
//
// @param key 目标
// @param intervals 周期间隔（秒数）
// @param limit 每周期最大额度
// @param consume 本次消费额度，可以传"0"表示不消费
//
// @return true：被限制，false：未被限制，当实际消费额度大于"0"时，一定会返回"false"
//
func (obj *GBRateLimitSet) CheckIsLimited(key interface{}, intervals, limit, consume int) bool {
	// 参数错误
	if intervals < 1 || limit < 1 {
		return true
	}
	// 加锁
	obj.elementsLock.Lock()
	defer obj.elementsLock.Unlock()
	// 获取对象
	v := obj.obtainElementOrCreate(key)
	// 检测
	return v.CheckIsLimited(intervals, limit, consume)
}
